import struct

import rtmp_msg_config as rmc


def IntByte(lenght: int, number: int):
    return number.to_bytes(lenght, byteorder='big', signed=False)


def IntByteLittle(lenght: int, number: int):
    return number.to_bytes(lenght, byteorder='little', signed=False)


def AmfNumToBytes(double, *args):
    send_data = IntByte(rmc.AmfTypeLen, rmc.AmfType[int])
    send_data += struct.pack("!d", double)
    return send_data


def AmfNullToBytes(*args):
    return IntByte(rmc.AmfTypeLen, rmc.AmfType[None])


def AmfStrToBytes(content, *args):
    send_data = IntByte(rmc.AmfTypeLen, rmc.AmfType[str])
    send_data += IntByte(rmc.AmfStrLen, len(content))
    send_data += StrByte(content)
    return send_data


def StrByte(content: str):
    return content.encode()


def StrKeyByte(content: str):
    ret = IntByte(rmc.AmfStrLen, len(content))
    ret += StrByte(content)
    return ret


def IntKeyByte(content: int):
    pass


def StrValueByte(content: str):
    ret = IntByte(rmc.AmfTypeLen, rmc.AmfType[str])
    return ret + StrKeyByte(content)


def IntValueByte(content: int):
    ret = IntByte(rmc.AmfTypeLen, rmc.AmfType[int])
    ret += IntByte(rmc.AmfIntLen, content)
    return ret


def FloatValueByte(content: float):
    return AmfNumToBytes(content)


def SetWindowAck(size: int):
    msg_head = rmc.MsgHead
    msg_head["ChuckStreamId"]["value"] = 2
    msg_head["BodySize"]["value"] = 4
    msg_head["TypeId"]["value"] = 5

    send_data = b""
    for i, j in msg_head.items():
        send_data += IntByte(j["len"], j["value"])
    body = IntByte(rmc.BodyLen, size)
    send_data += body
    return send_data


def PullSetWindowAck(size: int):
    msg_head = rmc.MsgHead
    msg_head["BodySize"]["value"] = 4
    msg_head["TypeId"]["value"] = 5

    send_data = b""
    for i, j in msg_head.items():
        send_data += IntByte(j["len"], j["value"])
    body = IntByte(rmc.BodyLen, size)
    send_data += body
    return send_data


def SetPeer(size: int):
    msg_head = rmc.MsgHead
    msg_head["BodySize"]["value"] = 5
    msg_head["TypeId"]["value"] = 6
    send_data = b""
    for i, j in msg_head.items():
        send_data += IntByte(j["len"], j["value"])
    body = IntByte(rmc.BodyLen, size)
    body += IntByte(rmc.LimitTypeLen, 2)
    send_data += body
    return send_data


def SetChunkSzie(size: int):
    msg_head = rmc.MsgHead
    msg_head["BodySize"]["value"] = 4
    msg_head["TypeId"]["value"] = 1
    send_data = b""
    for i, j in msg_head.items():
        send_data += IntByte(j["len"], j["value"])
    body = IntByte(rmc.BodyLen, size)
    send_data += body
    return send_data


def NetConnectionResult(*args):
    msg_head = rmc.MsgHead
    msg_head["ChuckStreamId"]["value"] = 3
    msg_head["BodySize"]["value"] = 4
    msg_head["TypeId"]["value"] = 20

    # body _result
    body = IntByte(rmc.AmfTypeLen, rmc.AmfType[str]) + \
           IntByte(rmc.AmfStrLen, len(rmc.NetConnectionResult)) + \
           StrByte(rmc.NetConnectionResult)

    # body TransactionID
    # body += IntByte(rmc.AmfTypeLen, rmc.AmfType[int]) + IntByte(rmc.TransactionIDLen, rmc.TransactionID)
    body += AmfNumToBytes(rmc.TransactionID)

    for i in args:
        body += IntByte(rmc.AmfTypeLen, rmc.AmfType[type(i)])
        func = {str: StrValueByte, int: IntValueByte, float: FloatValueByte}
        for j, k in i.items():
            body += StrKeyByte(j)
            body += func[type(k)](k)
        body += IntByte(rmc.AmfEndLen, rmc.AmfEnd)

    msg_head["BodySize"]["value"] = len(body)

    send_data = b""
    for i, j in msg_head.items():
        send_data += IntByte(j["len"], j["value"])
    send_data += body
    return send_data


def CreateSteam(content):
    msg_head = rmc.MsgHead
    msg_head["ChuckStreamId"]["value"] = 3
    msg_head["TypeId"]["value"] = 20
    func_dict = {str: AmfStrToBytes, int: AmfNumToBytes, None: AmfNullToBytes}
    body = b''
    for i in content:
        if i is None:
            body += func_dict[None](i)
        else:
            body += func_dict[type(i)](i)
    msg_head["BodySize"]["value"] = len(body)
    send_data = b""
    for i, j in msg_head.items():
        send_data += IntByte(j["len"], j["value"])
    send_data += body
    return send_data


def Obj(data):
    send_data = IntByte(rmc.AmfTypeLen, rmc.AmfType[dict])

    func = {str: StrValueByte, int: IntValueByte, float: FloatValueByte, bool: BollByte}

    for i, j in data.items():
        send_data += StrKeyByte(i) + func[type(j)](j)
    send_data += IntByte(rmc.AmfEndLen, rmc.AmfEnd)
    return send_data


def BollByte(b: bool):
    send_data = IntByte(rmc.AmfTypeLen, rmc.AmfType[bool])
    if b:
        send_data += IntByte(rmc.AmfTypeLen, rmc.AmfBoolTrue)
    else:
        send_data += IntByte(rmc.AmfTypeLen, rmc.AmfBoolFalse)
    return send_data


def Array(data):
    send_data = IntByte(rmc.AmfTypeLen, rmc.AmfType[list])
    send_data += IntByte(rmc.BodyLen, len(data))
    func = {str: StrValueByte, int: IntValueByte, float: FloatValueByte, bool: BollByte}
    for k in data:
        for i, j in k.items():
            send_data += StrKeyByte(i) + func[type(j)](j)
        # send_data += IntByte(rmc.AmfEndLen, rmc.AmfEnd)
    send_data += IntByte(rmc.AmfEndLen, rmc.AmfEnd)
    return send_data


def OnStatus(content):
    msg_head = rmc.MsgHead
    msg_head["ChuckStreamId"]["value"] = 4
    msg_head["TypeId"]["value"] = 20
    func_dict = {str: AmfStrToBytes, int: AmfNumToBytes, None: AmfNullToBytes, dict: Obj}
    body = b''
    for i in content:
        if i is None:
            body += func_dict[None](i)
        else:
            body += func_dict[type(i)](i)
    msg_head["BodySize"]["value"] = len(body)
    send_data = b""
    for i, j in msg_head.items():
        send_data += IntByte(j["len"], j["value"])
    send_data += body
    return send_data


def PullRecorded(v: int):
    msg_head = rmc.MsgHead
    msg_head["ChuckStreamId"]["value"] = 2
    msg_head["StreamId"]["value"] = 1
    msg_head["TypeId"]["value"] = 4
    msg_head["BodySize"]["value"] = 6
    body = IntByte(6, v)
    send_data = b""
    for i, j in msg_head.items():
        send_data += IntByte(j["len"], j["value"])
    send_data += body
    return send_data


def PlayReset(content):
    msg_head = rmc.MsgHead
    msg_head["ChuckStreamId"]["value"] = 8
    msg_head["StreamId"]["value"] = 1
    msg_head["TypeId"]["value"] = 20
    func_dict = {str: AmfStrToBytes, int: AmfNumToBytes, None: AmfNullToBytes, dict: Obj}
    body = b''
    for i in content:
        if i is None:
            body += func_dict[None](i)
        else:
            body += func_dict[type(i)](i)
    msg_head["BodySize"]["value"] = len(body)
    send_data = b""
    for i, j in msg_head.items():
        send_data += IntByte(j["len"], j["value"])
    send_data += body
    return send_data


def OnMetaData(content):
    msg_head = rmc.MsgHead
    msg_head["ChuckStreamId"]["value"] = 5
    msg_head["StreamId"]["value"] = 1
    msg_head["TypeId"]["value"] = 18
    func_dict = {str: AmfStrToBytes, int: AmfNumToBytes, None: AmfNullToBytes, dict: Obj, list: Array}
    body = b''
    for i in content:
        if i is None:
            body += func_dict[None](i)
        else:
            body += func_dict[type(i)](i)
    msg_head["BodySize"]["value"] = len(body)
    send_data = b""
    for i, j in msg_head.items():
        if i == 'StreamId':
            send_data += IntByteLittle(j["len"], j["value"])
        else:
            send_data += IntByte(j["len"], j["value"])
    send_data += body
    return send_data


def RtmpSampleAccess(content):
    msg_head = rmc.MsgHead
    msg_head["ChuckStreamId"]["value"] = 5
    msg_head["StreamId"]["value"] = 1
    msg_head["TypeId"]["value"] = 18
    func_dict = {str: AmfStrToBytes, int: AmfNumToBytes, None: AmfNullToBytes, dict: Obj, bool: BollByte}
    body = b''
    for i in content:
        if i is None:
            body += func_dict[None](i)
        else:
            body += func_dict[type(i)](i)
    msg_head["BodySize"]["value"] = len(body)
    send_data = b""
    for i, j in msg_head.items():
        send_data += IntByte(j["len"], j["value"])
    send_data += body
    return send_data


def StreamBegin(content):
    msg_head = rmc.MsgHead
    msg_head["ChuckStreamId"]["value"] = 2
    msg_head["StreamId"]["value"] = 0
    msg_head["TypeId"]["value"] = 4

    body = IntByte(rmc.StreamBeginLen, rmc.StreamBegin)
    msg_head["BodySize"]["value"] = len(body)
    send_data = b""
    for i, j in msg_head.items():
        send_data += IntByte(j["len"], j["value"])
    send_data += body
    return send_data


def BytesToIntBig(buf: bytes):
    return int.from_bytes(buf, byteorder="big")


def StrSerialization(content):
    amf_type = content[0]
    strlen = content[1:3]
    strlen = BytesToIntBig(strlen)
    ret = content[3: 3+strlen]
    return 3+strlen, ret.decode()


def IntSerialization(content):
    pass


def NoneSerialization(content):
    pass


def ObjKeySerialization(content):
    pass


def ObjStrSerialization(content):
    pass


def ObjIntSerialization(content):
    amftype = content[0]
    ret = struct.unpack("!d", content[1:9])
    ret = float('%.1f' % ret[0])
    index = 9
    return index, ret


def ObjBoolSerialization(content):
    amftype = content[0]
    ret = True if content[1] == rmc.AmfBoolTrue else False
    return 2, ret


def ObjSerialization(content):
    pass


def ListSerialization(content):
    amftype = content[0]
    arraylen = content[1:5]
    arraylen = BytesToIntBig(arraylen)
    content = content[5:]
    ret = {}
    func_dict = {str: StrSerialization, int: ObjIntSerialization, None: NoneSerialization, bool: ObjBoolSerialization}
    i = 0
    retindex = 5
    while i <= arraylen:
        if content == rmc.ConnectEnd:
            retindex += 3
            break
        index = 0
        keylen = BytesToIntBig(content[:2])
        index += 2
        key = content[index: index + keylen].decode()
        index += keylen

        idx, data = func_dict[rmc.AmfTypeValue[content[index]]](content[index:])
        index += idx
        ret[key] = data
        content = content[index:]
        retindex += index
        i += 1
    return retindex, ret


def PullDataFrame(content):
    bodysize = b""
    index = 0
    for i in content:
        if i == rmc.SetDataFrameChunkId and content[index + 7] == rmc.SetDataFrameTypeId:
            bodysize = content[index + 4: index + 7]
            break
        index += 1
    bodysize = int.from_bytes(bodysize, byteorder='big', signed=True)
    data = content[index + 12: index + 12 + bodysize]
    ret = []
    func_dict = {str: StrSerialization, int: IntSerialization, None: NoneSerialization, dict: ObjSerialization,
                 list: ListSerialization}
    i = 0
    while i < len(data):
        index, Serialization = func_dict[rmc.AmfTypeValue[data[i]]](data)
        ret.append(Serialization)
        data = data[index:]
    for i in ret:
        if type(i) == dict:
            print(i)
            return i





